﻿using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using System.Linq;
using ProjectBordspil.Models;
using System.Web.Security;
using System.Collections.Generic;

namespace ProjectBordspil.Controllers
{
    [Authorize]
    public class GameHub : Hub
    {
        AppDataContext context = new AppDataContext();

        List<string> connectedUsers = new List<string>();

        /// <summary>
        /// Adds a connected user to a group. Players playing the same instance of a particular game
        /// belong to the same group.
        /// </summary>
        public void Join(string groupId)
        {
            Groups.Add(Context.ConnectionId, groupId);
        }


        /// <summary>
        /// The function initialises the users that connect to the game as user1, user2 and currentPlayer
        /// </summary>
        public void InitialiseUsers(string groupName)
        {
            int nGameId = 0;
            string user1;

            if (Int32.TryParse(groupName, out nGameId))
            {
                user1 = GetUserOne(nGameId);

                Clients.Group(groupName).showUserOne(user1);

                //A dialog that should disapear when user2 connects (not implemented)
                Clients.Group(groupName).WaitForUser2();

                string connectedUser = (Context.User.Identity.Name).ToString();

                //if connected user is not user1
                if (connectedUser != user1)
                {
                    var currentGame = GetCurrentgame(nGameId); 

                    //Make sure that no more then two players join a game
                    if (currentGame.user2 == null)
                    {
                        currentGame.user2 = Context.User.Identity.Name;

                        GetCurrentgame(nGameId).NumberOfPlayers = 2;

                        Clients.Group(groupName).showUserTwo(currentGame.user2);

                        currentGame.initOponents = true;

                        Clients.Group(groupName).Close();

                        context.SaveChanges();
                    }

                    //A function that chooses the starting player randomly
                    GetWhoStars(nGameId, groupName);

                    context.SaveChanges();
                }
            }
        }

        /// <summary>
        /// Client calls this function when a card is clicked
        /// </summary>
        public void ClickCard(string userName, string groupName, string cardId)
        {
            int nCardId = 0;
            int nGameId = 0;

            if (Int32.TryParse(cardId, out nCardId) && Int32.TryParse(groupName, out nGameId))
            {
                var currentGame = GetCurrentgame(nGameId);

                if(currentGame.initOponents == true)
                {
                    string currentPlayer = GetCurrentPLayer(nGameId);

                    if (userName == currentPlayer)
                    {
                        SetButtonStatusToUp(nCardId);

                        Clients.Group(groupName).ShowCard(cardId);

                        var shownCards = (from p in context.MemImageInfo where (p.idGameInstance == nGameId) && (p.status == "up") select p).ToList();

                        //Has the player turned two cards?
                        if (shownCards.Count() == 2)
                        {
                            //User gets a correct pair of cards
                            if (shownCards[0].theImageObject.value == shownCards[1].theImageObject.value)
                            {
                                UpdateScore(userName, nGameId);
                                var user1Score = GetUserOneScore(nGameId);
                                var user2Score = GetUserTwoScore(nGameId);

                                Clients.Group(groupName).ShowUpdateScore(user1Score, user2Score);

                                shownCards[0].status = "found";
                                shownCards[1].status = "found";

                                context.SaveChanges();

                                IsGameFinished(nGameId, nCardId, groupName);

                            }
                            //User picks two cards that are not the same
                            else
                            {

                                currentPlayer = GetCurrentPLayer(nGameId);
                                var user1 = GetUserOne(nGameId);

                                if (currentPlayer == user1)
                                {
                                    SetUserTwoToCurrent(nGameId);
                                    Clients.Group(groupName).Player2Turn();
                                }
                                else
                                {
                                    SetUserOneToCurrent(nGameId);
                                    Clients.Group(groupName).Player1Turn();
                                }

                                System.Threading.Thread.Sleep(1000);

                                shownCards[0].status = "down";
                                Clients.Group(groupName).HideCards(shownCards[0].id);
                                shownCards[1].status = "down";
                                Clients.Group(groupName).HideCards(shownCards[1].id);

                                context.SaveChanges();
                        }
                    }
                    }
                }
            }
        }

        /// <summary>
        /// Retreive the current game instance from database
        /// </summary>
        public ActiveGames GetCurrentgame(int nGameId)
        {
            var GetCurrent = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();
            return GetCurrent;
        }

        /// <summary>
        /// Function that gets user 1 from database
        /// </summary>
        public string GetUserOne(int nGameId)
        {
            var getUser1 = (from p in context.ActiveGames where p.id == nGameId select p.user1).SingleOrDefault();
            return getUser1;
        }

        /// <summary>
        /// Function that gets user 2 from database
        /// </summary>
        public string GetUserTwo(int nGameId)
        {
            var getUser2 = (from p in context.ActiveGames where p.id == nGameId select p.user2).SingleOrDefault();
            return getUser2;
        }

        /// <summary>
        /// Function that sets the current user to user1
        /// </summary>
        public void SetUserOneToCurrent(int nGameId)
        {
            string nameOfUserOne = ((from p in context.ActiveGames where p.id == nGameId select p.user1).SingleOrDefault()).ToString();
            var currentGameObject = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();

            currentGameObject.currentPlayer = nameOfUserOne;

            context.SaveChanges();
        }

        /// <summary>
        /// Function that sets the current user to user 2
        /// </summary>
        public void SetUserTwoToCurrent(int nGameId)
        {
            string nameOfUserTwo = ((from p in context.ActiveGames where p.id == nGameId select p.user2).SingleOrDefault()).ToString();
            var currentGameObject = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();

            currentGameObject.currentPlayer = nameOfUserTwo;

            context.SaveChanges();
        }

        /// <summary>
        /// Function that gets the player whos turn it is in the game
        /// </summary>
        public string GetCurrentPLayer(int nGameId)
        {
            var GetCurrent = ((from p in context.ActiveGames where p.id == nGameId select p.currentPlayer).SingleOrDefault()).ToString();
            return GetCurrent;
        }

        /// <summary>
        /// Function that sets the button (card) status that was clicked to "up" in the database
        /// </summary>
        public void SetButtonStatusToUp(int nCardId)
        {
            var button = (from p in context.MemImageInfo where p.id == nCardId select p).SingleOrDefault();
            button.status = "up";

            context.SaveChanges();
        }

        /// <summary>
        /// The function updates the score of the player who just found a matching pair of cards
        /// </summary>
        public void UpdateScore(string userName, int nGameId)
        {
            var user1 = GetUserOne(nGameId);

            var currentPlayer = GetCurrentPLayer(nGameId); 

            if(user1 == currentPlayer)
            {
                SetUserOneScore(nGameId, userName);
            }
            else
            {
                SetUserTwoScore(nGameId, userName);
            }

            context.SaveChanges();
        }

        /// <summary>
        /// Gets the current score of user1 from database
        /// </summary>
        public int GetUserOneScore(int nGameId)
        {
            var GetScore = (from p in context.ActiveGames where p.id == nGameId select p.userOneScore).SingleOrDefault();
            
            return GetScore;
        }

        /// <summary>
        /// Gets the current score of user2 from database
        /// </summary>
        public int GetUserTwoScore(int nGameId)
        {
            var GetScore = (from p in context.ActiveGames where p.id == nGameId select p.userTwoScore).SingleOrDefault();

            return GetScore;
        }

        /// <summary>
        /// Function adds 1 to user1's score
        /// </summary>
        public void SetUserOneScore(int nGameId, string userName)
        {
            var gameInstance = GetCurrentgame(nGameId);
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();
            
            gameInstance.userOneScore += 1;
            aUser.scoreMem += 1;

            context.SaveChanges();
        }

        /// <summary>
        /// Function adds 1 to user2's score
        /// </summary>
        public void SetUserTwoScore(int nGameId, string userName)
        {
            var gameInstance = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();

            gameInstance.userTwoScore += 1;
            aUser.scoreMem += 1;

            context.SaveChanges();
        }

        /// <summary>
        /// Function that checks if game is over
        /// </summary>
        public void IsGameFinished(int nGameId,int nCardId, string groupName)
        {
            var finished = (from p in context.MemImageInfo where (p.idGameInstance == nGameId) && (p.status == "found") select p).ToList();
            
            if (finished.Count == 28)
            {
                var winner = GetWinner(nGameId);
                var scoreOfWinner = GetWinnerScore(nGameId);

                Clients.Group(groupName).GameFinished(nCardId, winner, scoreOfWinner); 

                SetGameToFinished(nGameId);

                context.SaveChanges();
                
            }
        }

        /// <summary>
        /// Change game from finished=false to finished=true in database
        /// </summary>
        public void SetGameToFinished(int nGameId)    
        {
            var finishedGame = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();
            finishedGame.finished = true;

            context.SaveChanges();
        }

        /// <summary>
        /// Gets the winning score
        /// </summary>
        public int GetWinnerScore(int nGameId)
        {
            var user1 = GetUserOne(nGameId);
            var user2 = GetUserTwo(nGameId);
            var user1Score = GetUserOneScore(nGameId);
            var user2Score = GetUserTwoScore(nGameId);

            if (user1Score > user2Score)
            {
                return user1Score;
            }
            else if (user1Score < user2Score)
            {
                return user2Score;
            }
            else
            {
                return user1Score;
            }
        }

        /// <summary>
        /// Gets the name of the winner
        /// </summary>
        public string GetWinner(int nGameId)
        {
            var user1 = GetUserOne(nGameId);
            var user2 = GetUserTwo(nGameId);
            var user1Score = GetUserOneScore(nGameId);
            var user2Score = GetUserTwoScore(nGameId);

            if (user1Score > user2Score)
            {
                return user1;
            }
            else if (user1Score < user2Score)
            {
                return user2;
            }
            else
            {
                //if there is a tie...
                return (user1 + user2);
            }
        }

        /// <summary>
        /// Randomizes which player starts and sets him to currentPlayer
        /// </summary>
        public void GetWhoStars(int nGameId, string groupName)
        {
            List<int> whoStarts = new List<int>();
            whoStarts.Add(1); //user1
            whoStarts.Add(2); //user2

            whoStarts = whoStarts.OrderBy(emp => Guid.NewGuid()).ToList();

            if (whoStarts[0] == 1) // if this is true, user1 starts the game
            {
                //sets currentPlayer as user1
                SetUserOneToCurrent(nGameId);
                Clients.Group(groupName).Player1Starts();
            }
            else //if this is true, user2 starts the game
            {
                //sets currentPlayer as user2
                SetUserTwoToCurrent(nGameId);
                Clients.Group(groupName).Player2Starts();
            }
        }
    }
}